<template>
  <div class="chat-box">
    <div v-if="messageList.length > 0" class="chat-list">
      <template v-for="(item, index) in messageList" :key="index">
        <div
          class="chat-item bot"
          :class="`message-${index}`"
          v-if="item.role === 'ai'"
        >
          <n-avatar
            class="avatar"
            :style="{
              backgroundColor: '#10A37F'
            }"
          >
            AI
          </n-avatar>
          <div class="content">
            <n-spin size="small" v-if="item.status === 'loading'" />
            <v-md-preview
              :text="item.content"
              @copy-code-success="handleCopyCodeSuccess"
              v-else-if="item.status === 'success'"
            ></v-md-preview>
            <div v-else-if="item.status === 'fail'">
              <n-space align="center">
                <span style="color: red">{{ item.content }}</span>
                <n-button
                  :disabled="getMsgLoading"
                  type="primary"
                  @click="handleToReSendMsg(index)"
                >
                  重新发送
                </n-button>
              </n-space>
            </div>
          </div>
        </div>
        <div
          class="chat-item user"
          :class="`message-${index}`"
          v-else-if="item.role === 'user'"
        >
          <div class="content">
            {{ item.content }}
          </div>
          <n-avatar
            class="avatar"
            :style="{
              backgroundColor: '#B49356'
            }"
          >
            U
          </n-avatar>
        </div>
      </template>
    </div>
    <n-space v-else style="height: 100%" align="center" justify="center">
      <n-result status="418" title="喝喝茶聊聊天" description="您想聊点什么吗">
        <template #footer>
          <n-button
            @click="
              () => {
                chatInputRef?.focus()
              }
            "
            >点我开始聊天</n-button
          >
        </template>
      </n-result>
    </n-space>
    <div class="input-area">
      <n-input-group>
        <n-input
          ref="chatInputRef"
          size="large"
          class="chat-input"
          v-model:value="chatContent"
          placeholder="请输入"
          clearable
          maxlength="500"
          @keydown.enter="handleToSendMsg"
          :disabled="getMsgLoading"
        />
        <n-button
          class="chat-button"
          type="primary"
          size="large"
          @click="handleToSendMsg"
          :disabled="getMsgLoading"
        >
          发送
        </n-button>
      </n-input-group>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { InputInst } from 'naive-ui'
import { getReply } from '@/api/home'
import { MessageItem } from './type'
// 输入框实例
const chatInputRef = ref<InputInst | null>(null)

// 消息列表
const messageList: Ref<MessageItem[]> = ref([])

// 聊天内容
const chatContent = ref('')

// 输入框加载动画
const getMsgLoading = computed(() => {
  return (
    messageList.value.length > 0 &&
    messageList.value.some((item) => item.status === 'loading')
  )
})

// 发送消息
const handleToSendMsg = async () => {
  try {
    if (!chatContent.value) {
      return (window as any).$message.error('请输入聊天内容!')
    } else {
      messageList.value.push({
        role: 'user',
        content: chatContent.value,
        status: 'success'
      })
      chatContent.value = ''
      messageList.value.push({
        role: 'ai',
        content: '',
        status: 'loading'
      })
      const { data } = await getReply({
        query: messageList.value[messageList.value.length - 2].content
      })
      updateMsgContent(messageList.value.length - 1, data, true)
    }
  } catch (e) {
    messageList.value[messageList.value.length - 1] = {
      role: 'ai',
      content: (e as string) || '获取消息失败！',
      status: 'fail'
    }
  } finally {
    await nextTick()
    chatInputRef.value?.focus()
  }
}

// 更新消息（打字机效果）
const updateMsgContent = (
  index: number,
  content: string,
  animated: boolean = false,
  totalDuration: number = 2000
) => {
  if (!animated) {
    messageList.value[index] = {
      role: 'ai',
      content: content,
      status: 'success'
    }
  } else {
    let contentList = content.split('')
    let contentListLen = contentList.length
    let singleDuration = 50 // 单次间隔
    let timeCount = Math.ceil(totalDuration / singleDuration) // 总输出次数
    let singleWordCount = Math.ceil(contentListLen / timeCount)
    let _content = ''
    let _start = 0
    let _end = singleWordCount
    for (let i = 0; i < timeCount; i++) {
      setTimeout(() => {
        _content += content.slice(_start, _end)
        _start = _end
        _end += singleWordCount
        messageList.value[index] = {
          role: 'ai',
          content: _content,
          status: 'success'
        }
      }, i * singleDuration)
    }
  }
}

// 重新发送
const handleToReSendMsg = async (index: number) => {
  try {
    messageList.value[index] = {
      role: 'ai',
      content: '',
      status: 'loading'
    }
    const { content } = messageList.value[index - 1]
    const { data } = await getReply({ query: content })
    updateMsgContent(index, data, true)
  } catch (e) {
    messageList.value[index] = {
      role: 'ai',
      content: (e as string) || '获取消息失败！',
      status: 'fail'
    }
  }
}

// 复制代码
const handleCopyCodeSuccess = () => {
  return (window as any).$message.success('复制成功!')
}

// 监听消息列表
watch(
  messageList,
  async (list) => {
    await nextTick()
    document
      .querySelector(`.message-${list.length - 1}`)
      ?.scrollIntoView({ behavior: 'smooth' })
  },
  { deep: true }
)
</script>
<style lang="scss" scoped>
.chat-box {
  position: relative;
  height: calc(100vh - 40px);

  .chat-list {
    height: 100%;
    padding-bottom: 50px;
    overflow-y: scroll;

    .bot-item {
      display: flex;
      justify-content: flex-start;
      margin-bottom: 20px;
      position: relative;
      padding: 0 40px;

      .avatar {
        position: absolute;
        top: 0;
        left: 0;
      }

      .content {
        max-width: 100%;
        position: relative;
        background: #efefef;
        padding: 6px;
        border-radius: 4px;
        word-break: break-all;
        z-index: 1;

        &::before {
          content: '';
          position: absolute;
          top: 12px;
          left: -3px;
          display: inline-block;
          height: 10px;
          width: 10px;
          background: #efefef;
          transform: rotate(45deg);
          z-index: -1;
        }

        :deep(.vuepress-markdown-body) {
          padding: 0;
          background: #efefef;
        }
      }
    }

    .self-item {
      display: flex;
      justify-content: flex-end;
      margin-bottom: 20px;
      position: relative;
      padding: 0 40px;

      .avatar {
        position: absolute;
        top: 0;
        right: 0;
      }

      .content {
        position: relative;
        background: #efefef;
        padding: 6px;
        border-radius: 4px;
        word-break: break-all;
        z-index: 1;

        &::before {
          content: '';
          position: absolute;
          top: 12px;
          right: -3px;
          display: inline-block;
          height: 10px;
          width: 10px;
          background: #efefef;
          transform: rotate(45deg);
          z-index: -1;
        }
      }
    }

    .chat-item {
      display: flex;
      justify-content: flex-start;
      position: relative;
      padding: 10px 10px 10px 50px;

      .avatar {
        position: absolute;
        top: 10px;
        left: 10px;
      }
      .content {
        width: 100%;
        position: relative;
        padding: 6px;
        word-break: break-all;
        :deep(.vuepress-markdown-body) {
          padding: 0;
        }
      }
    }

    .user {
      background: #ffffff;
      :deep(.vuepress-markdown-body) {
        background: #ffffff;
      }
    }

    .bot {
      background: #efefef;
      :deep(.vuepress-markdown-body) {
        background: #efefef;
      }
    }
  }

  .input-area {
    width: 100%;
    padding-top: 10px;
    background: #ffffff;
    position: absolute;
    bottom: 0;
    left: 0;
    z-index: 1;

    :deep(.n-input-group) {
      box-shadow: 0 1px 2px -2px rgb(0 0 0 / 8%), 0 3px 6px 0 rgb(0 0 0 / 6%),
        0 5px 12px 4px rgb(0 0 0 / 4%);
    }

    .chat-input {
      width: 100%;
    }
  }
}
</style>
